package net.zjitc.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import net.zjitc.common.lang.Const;
import net.zjitc.exception.CaptchaException;
import net.zjitc.security.model.AuthenticationBean;
import net.zjitc.utils.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;

/**
 * @Author 毛钰深
 * @Create 2022/2/28 10:11
 */
public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    @Autowired
    private LoginFailureHandler loginFailureHandler;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //attempt Authentication when Content-Type is json

        try {
            if (request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)
                    || request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {

                //use jackson to deserialize json
                ObjectMapper mapper = new ObjectMapper();
                UsernamePasswordAuthenticationToken authRequest = null;
                try {
                    InputStream is = request.getInputStream();

                    AuthenticationBean authenticationBean = mapper.readValue(is, AuthenticationBean.class);

                    //校验验证码(redis中必须已经存有相应的验证码!!)
                    try {

                        //测试使用
                        validate(authenticationBean);

                        //抛出异常则catch,否则验证码正确,放行


                    } catch (CaptchaException e) {
                        //捕获到异常的话,就交给认证失败处理器
                        loginFailureHandler.onAuthenticationFailure(request, response, e);
                    }

                    authRequest = new UsernamePasswordAuthenticationToken(
                            authenticationBean.getUsername(), authenticationBean.getPassword());
                } catch (Exception e) {
                    logger.error(e.getMessage());
                    authRequest = new UsernamePasswordAuthenticationToken(
                            "", "");
                } finally {
                    setDetails(request, authRequest);
                }
                return this.getAuthenticationManager().authenticate(authRequest);
            }

        }catch (Exception e){
            logger.error(e.getMessage());
        }
        //transmit it to UsernamePasswordAuthenticationFilter
        return super.attemptAuthentication(request, response);
    }

    /**
     * 验证码校验
     * @param authenticationBean
     */
    private void validate(AuthenticationBean authenticationBean) {

        String code = authenticationBean.getCode();
        String key = authenticationBean.getKey();

        if (StringUtils.isBlank(code) || StringUtils.isBlank(key)){
            throw new CaptchaException("验证码错误");
        }

        if(!code.equalsIgnoreCase((String) redisUtil.hget(Const.CAPTCHA_KEY,key))){
            throw new CaptchaException("验证码错误");
        }

        //一次性使用的验证码,校验后立即销毁
        redisUtil.hdel(Const.CAPTCHA_KEY,key);

    }
}